/**
 * 乐观加锁(Optimistic locking)
 *
 * @author fireway
 * @since 2019年 04月 22日 星期一 06:46:48 CST
 */
package concurrency;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class FastSimulation {
    private static final int N_ELEMENTS = 100000;

    private static final int N_GENES = 30;

    private static final AtomicInteger[][] GRID = new AtomicInteger[N_ELEMENTS][N_GENES];

    private static final int N_EVOLVERS = 50;

    private static Random sRand = new Random(47);

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();

        for(int i = 0; i < N_ELEMENTS; i++) {
            for(int j = 0; j < N_GENES; j++) {
                GRID[i][j] = new AtomicInteger(sRand.nextInt(1000));
            }
        }

        for(int i = 0; i < N_EVOLVERS; i++) {
            exec.execute(new Evolver());
        }

        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        exec.shutdownNow();
    }

    static class Evolver implements Runnable {
        @Override
        public void run() {
            while(!Thread.interrupted()) {
                // Randomly select an element to work on
                int element = sRand.nextInt(N_ELEMENTS);
                for(int i = 0; i < N_GENES; i++) {
                    int previous = element - 1;
                    if(previous < 0) {
                        previous = N_ELEMENTS - 1;
                    }
                    int next = element + 1;
                    if(next >= N_ELEMENTS) {
                        next = 0;
                    }
                    int oldvalue = GRID[element][i].get();
                    // Perform some kind of modeling calculation
                    int newvalue = oldvalue + GRID[previous][i].get() + GRID[next][i].get();
                    // Average the three values
                    newvalue /= 3;
                    if (!GRID[element][i].compareAndSet(oldvalue, newvalue)) {
                        // Policy here to deal with failure. Here, we
                        // just report it and ignore it; our model
                        // will eventually deal with it.
                        System.out.println("Old value changed from " + oldvalue);
                    }
                }
            }
        }
    }
}
